# -- coding = utf-8 --
# @Time : 2021/4/1 11:33
# @Author : yk
# @File : Boss.py
# @Software: PyCharm

from bs4 import BeautifulSoup   #用于网页解析
import re        #正则表达式，进行文字匹配
import urllib.request,urllib.error  #制定url，获取网页数据
import xlwt      #进行excel操作
import time
from functools import reduce   #合并列表中的元素

def main():
    baseurl = "https://www.zhipin.com/c100010000/?query=%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD&page="  #给一个目标网站

    # 1、爬取网页
    dataList = getData(baseurl)
    savePath = "NEW.xls"   #保存数据文件名，每次爬取一个表（对应一页数据），每爬取一页需改值，后运行
    #保存数据
    saveData(dataList, savePath)

    # askURL("https://www.zhipin.com/c100010000/?query=%E8%BD%AF%E4%BB%B6&page=1")  #用于测试获取网页信息

#定义全局变量的地方：
#获取招聘职位名称的规则
PDName = re.compile(r'<span class="job-name"(.*)>(.*)</a></span>')
#获取公司地区的规则
PDArea = re.compile(r'<span class="job-area">([\u4E00-\u9FA5A-Za-z0-9-^()-_·]+)</span>')
#获取发布时间的规则（具体爬取日期必须提前告知，会出现爬取数据为“发布于昨天”的情况）
PDTime = re.compile(r'<span class="job-pub-time">([\u4E00-\u9FA50-9_]+)</span>')
#获取薪资待遇的规则
PDMoney = re.compile(r'<span class="red">([\u4E00-\u9FA5A-Za-z0-9-^()-_·]+)</span>')
#获取学历要求的规则
PDRecord = re.compile(r'<em class="vline"></em>([\u4E00-\u9FA5A-Za-z0-9-/]+)</p>') #[\u4E00-\u9FA5A-Za-z0-9-/]+
#获取公司名称的规则(需要注明角标)
PDCompany = re.compile(r'<h3 class="name">(.*)>(.*)</a></h3>') #[\u4E00-\u9FA5A-Za-z0-9 ]+
#获取公司类型的规则
PDComType = re.compile(r'<a class="false-link"(.*)>(.*)</a>')
#获取公司规模的规则(需要注明角标)
PDScope = re.compile(r'<p>(.*)<em class="vline"></em>(.*)</p>')
#获取职位标签的规则
PDTags = re.compile(r'<span class="tag-item">(.*)</span>')
#获取公司福利的规则
PDInfo =re.compile(r'<div class="info-desc">(.*)</div>')

#1、获取网页,解析数据
def getData(baseurl):
    url2 = "&ka=page-"
    dataList = []
    i = 1     #因为循环失效  我也不知道为什么  for和while都试过了，没用，所以这里每一次爬取需与文件名后缀数字同步
    for i in range(1,11):  #调用获取页面信息askURL函数 10次
        url = baseurl + str(i) + url2 + str(i)
        html = askURL(url)  #保存获取到的网页源码
        #逐一解析数据   分析每一个招聘信息的li  逐步分析  逐步保存
        soup = BeautifulSoup(html,"html.parser")
        for item in soup.find_all('div',class_="job-primary"):#查找<div class="job-primary">的标签，精准定位，形成列表   class_表示标签属性
            time.sleep(0.8)

            # print(item) #用于测试打印指定标签内所有信息内容使用

            data = []  # 保存一则招聘公告的所有信息
            item = str(item)  # 方便解析，将所有类型转换为字符串
            # Link  = re.findall(PDScope,item)[1]
            # print(Link)  #测试正则标签内容提取
            #添加招聘职位
            Name = re.findall(PDName, item)  # re库使用正则表达式查找指定的标签
            data.append(Name[0][1])  #由于Name是一个列表，且里面保存了一个元组数据，我们需要元组数据的第二个数据
            #添加公司地点
            Area = re.findall(PDArea,item)
            data.append(Area)
            #添加招聘时间
            Time =re.findall(PDTime,item)
            data.append(Time)
            #添加薪资待遇
            Money = re.findall(PDMoney,item)
            data.append(Money)
            #添加学历要求
            Record = re.findall(PDRecord,item)
            data.append(Record[0]) #通过遍历可以得知Record是一个列表，又两个元素，取第一个
            #添加公司名字
            Company = re.findall(PDCompany,item)
            data.append(Company[0][1]) #和招聘职位一样，使用正则表达式保存下来的Company是一个列表中包含元组的数据类型，所以使用[][]来查找数据
            #保存公司类型
            ComType = re.findall(PDComType,item)
            data.append(ComType[0][1]) #同上
            #保存公司规模
            # re库使用正则表达式来查找相应的数据   [0]表示只拿符合要求的第一个
            Scope = re.findall(PDScope,item)[1]  #第一个该标签数学历要求，需要第二个<em>标签
            data.append(Scope[1])  #同上，需要的是元组中的第二个数据
            #保存职位标签
            tags = re.findall(PDTags,item) #这里职位标签需要将列表中每一个元素分隔开来
            t = reduce(lambda x, y: str(x) + '，' + str(y), tags) #将tags列表中的所有元素合并为一个以逗号分隔的字符串
            Tags = []
            Tags.append(t)  #将合并后的字符串存入列表
            data.append(Tags)  #将修改好的职位标签保存进data
            #保存公司福利
            Info = re.findall(PDInfo,item)
            data.append(Info)
            #i += 1
            print("第一页爬取中")
            dataList.append(data)
        print('这是第%d页'%i)
    return dataList

#得到指定的一个URL的网页信息
def askURL(url):
    head = {               #模拟浏览器头部信息，向豆瓣服务器发送信息

         "user-agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"
        ,
         "cookie" : "Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1623031609; wt2=DsHsWPYL4nUp2godSvIIJjonbAKWw__Prs3T9kh6dyIDL19kvPh4DhXNfMfdxS6koaE9flcPZAXeqjhXJ0quRjQ~~; __l=l=%2Fwww.zhipin.com%2Fweb%2Fgeek%2Frecommend%3Frandom%3D1623031697860&r=&g=&s=3&friend_source=0&s=3&friend_source=0; _bl_uid=Fjkm9pw9lyhz1n6dv13df3s3L8Og; lastCity=100010000; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1623031756; __c=1623031609; __a=96281753.1623031609..1623031609.7.1.7.7; __zp_stoken__=d333cOHhmLgYqajAeEUQSQUg2ZjEZUns1PFM7ECo3LCoWWkM4JnZtUXkxRGVtXURlNm5HWS53KB96Onc9D0IrdlIGZAM8Fz5pEi4XKlVwXScPNgkSUGUDVHBYUycIW3EYBk1XZAYGADQYcmUd; geek_zp_token=V1RtsgF-L-21ZgXdNrzB0bKi-55D_Vwg~~"

    }
    #用户代理：表示告诉boss服务器，我们是什么类型的机器、浏览器（本质上是告诉浏览器，我们可以接收什么水平的文件内容）
    #urllib.request.Request 生成请求
    req = urllib.request.Request(url=url,headers=head)
    html = ""
    try:                   #异常的处理，重要
        # 发起请求，获取响应
        resp = urllib.request.urlopen(req,timeout=1.5)
        # read获取页面内容 设置编码格式
        html = resp.read().decode('utf-8')
        # print(html)  #用于测试打印网页，测试完就注释
    except urllib.error.URLError as e:
        if hasattr(e,'code'):
            print(e.code)
        if hasattr(e,'reason'):
            print(e.reason)
    return html

# 3.1、保存数据
def saveData(dataList, savePath):  # 这个函数的参数解读为将dataList保存到savePath中
    print('开始保存')
    book = xlwt.Workbook(encoding="utf-8", style_compression=0)  # 创建workbook对象
    sheet = book.add_sheet('Sheet1', cell_overwrite_ok=True)  # 创建工作表，每给xls文件中的表名都是Sheet1
    col = ["招聘职位名称","公司所在地区","发布时间","薪资待遇","学历要求","公司名称","公司类型","公司规模","职位标签","公司福利"]
    for i in range(0,10):
        sheet.write(0,i,col[i]) #这里只是把列名写入了一个空表格，写入数据要重启一个新循环
    for i in range(0,30):    #这里保存数据只能一页一页的保存，一页数据30条  注意在第一次保存之后就不需要创建表代码，只需要保存数据
        print("第%d条" %(i+1))
        data = dataList[i]  #取出一则招聘公告的数据（一个公告列表中有10条数据）
        for j in range(0,10):
            sheet.write(i+1,j,data[j])#循环地将10条数据存入sheet表中

    book.save(savePath)   #保存数据


    print('保存完成')

if __name__ == "__main__":
    main()